#pragma once
#include "data.hpp"
#include "httplib.h"

extern cloud::DataManager* _data; //声明外部变量

namespace cloud
{
  class Service
  {
  private:
    std::string _server_ip; //监听ip
    int _server_port; //监听端口
    std::string _download_prefix; //下载请求前缀
    httplib::Server _server; //服务端
  private:
    //上传文件
    static void Upload(const httplib::Request& req, httplib::Response& res)
    {
      auto ret = req.has_file("file");
      //没有上传文件
      if(ret == false)
      {
        res.status = 400;
        return ;
      }
      //有上传文件
      const auto& file = req.get_file_value("file"); //获取此文件的属性内容
      Config* conf = Config::_get_instance();
      std::string real_path = conf->GetBackDir() + file.filename; //获取该文件的真实存储路径
      //将上传的文件的内容写入
      FileUtil fu(real_path);
      fu.SetFileContent(file.content);
      //获取上传文件的属性内容
      BackupInfo info; 
      info.InitBackupInfo(real_path);
      _data->Insert(info); //对文件进行备份
      res.status = 200;
      return ;
    }
    //显示上传备份列表
    static void ShowList(const httplib::Request& req, httplib::Response& res)
    {
      //获取所有备份文件
      std::vector<BackupInfo> arr;
      _data->GetAll(arr);
      //组织html代码
      std::stringstream ss;
      ss << "<html><head><title>Download</title></head><body><h1>Download</h1><table>";
      ss << "<tr>";                                                                            
      ss << "<td align='left'><h2>file_name</h2></td>"; 
      ss << "<td align='left'><h2>mtime</h2></td>";
      ss << "<td align='left'><h2>size</h2></td>";
      ss << "</tr>";
      for(const auto& e : arr)
      {
        ss << "<tr>";
        ss << "<td align='left'><a href='" << e.url << "'>" << FileUtil(e.real_path).FileName() <<"</a></td>";
        ss << "<td align='left'>" << ctime(&e.mtime) << "</td>";
        ss << "<td align='left'>" << e.fsize / 1024 << "k</td>";
        ss << "</tr>";
      }
      ss << "</table></body></html>";
      //设置回应信息
      res.body = ss.str();
      res.set_header("Content-Type", "text/html");
      res.status = 200;
      return ;
    }
    
    //自定义ETag标识("文件名-文件最后修改时间-文件大小")
    static std::string GetETag(const BackupInfo& info)
    {
      FileUtil fu(info.real_path);
      std::string str = fu.FileName();
      str += "-";
      str += std::to_string(info.mtime);
      str += "-";
      str += std::to_string(info.fsize);
      return str;
    }

    //下载文件
    static void Download(const httplib::Request& req, httplib::Response& res)
    {
      //判断请求下载的文件是否已压缩,如果已压缩,进行解压
      BackupInfo info;
      _data->GetOneByURL(req.path, info);
      if(info.pack_flag == true)
      {
        FileUtil fu(info.pack_path);
        fu.UnCompress(info.real_path); //解压到哪
        fu.Remove(); //解压后删除压缩文件
        info.pack_flag = false; //变更压缩标志
        _data->Updata(info); //更新_table
      }
      FileUtil fu(info.real_path);
      bool flag = false; //判断当前请求是否是断点续传
      //If-Range:断点续传的标志
      if(req.has_header("If-Range"))
      {
        std::string old_etag = req.get_header_value("If-Range");
        //判断断点续传的请求文件是否未被修改
        if(old_etag == GetETag(info))
        {
          flag = true;
        }
      }
      //如果请求是断点续传且请求的文件未被修改,那么执行断点续传,否则重新下载该文件
      if(flag)
      {
        fu.GetFileContent(res.body); //将文件写入响应
        res.set_header("Accept-Ranges", "bytes"); //断点续传
        res.set_header("ETag", GetETag(info)); //ETag:资源的唯一标识
        res.set_header("Content-Type", "application/octet-stream"); 
        res.status = 206; //断点续传区间请求回应的是206
      }
      else
      {
        fu.GetFileContent(res.body); //将文件写入响应
        res.set_header("Accept-Ranges", "bytes"); //断点续传
        res.set_header("ETag", GetETag(info)); //ETag:资源的唯一标识
        res.set_header("Content-Type", "application/octet-stream"); 
        res.status = 200;
      }
      return ;
    }
  public:
    Service()
    {
      Config* conf = Config::_get_instance();
      _server_ip = conf->GetServerIp();
      _server_port = conf->GetServerPort();
      _download_prefix = conf->GetURLPrefix();
    }

    //业务处理
    bool RunModule()
    {
      _server.Post("/upload", Upload);
      _server.Get("/showlist", ShowList);
      _server.Get("/", ShowList);
      _server.Get("/download/(.*)", Download); //(.*)是一个正则表达式,表示匹配 /download/ 后面的任意字符
      _server.listen("0.0.0.0", _server_port);
      return true;
    }
  };
}
